THREE.PanoControls = function(camera, domElement, material) {
    this.camera = camera;
    camera.target = new THREE.Vector3();
    this.target = camera.target;
    this.domElement = domElement !== undefined ? domElement : document;

    this.autoRotate = false; //是否允许自动旋转
    this.isUserInteracting = false; //是否在交互过程中
    this.isDragPanoImage = false; //是否允许拖拽载入全景

    this.enableDamping = true;
    this.dampingFactor = 0.15;
    var sphericalDelta = new THREE.Spherical();

    var onMouseDownMouseX = 0,
        onMouseDownMouseY = 0,
        lon = 0,
        onMouseDownLon = 0,
        lat = 0,
        onMouseDownLat = 0,
        phi = 0,
        theta = 0;
    var rotateSpeed = 0.1;
    var zoomSpeed = 1;
    var minZoom = 10;
    var maxZoom = 75;

    var dollyStart = new THREE.Vector2();
    var dollyEnd = new THREE.Vector2();
    var dollyDelta = new THREE.Vector2();

    var scope = this;
    //

    function onPointerStart(event) {
        event.preventDefault();

        scope.isUserInteracting = true;

        var clientX = event.clientX || event.touches[0].clientX;
        var clientY = event.clientY || event.touches[0].clientY;

        onMouseDownMouseX = clientX;
        onMouseDownMouseY = clientY;

        onMouseDownLon = lon;
        onMouseDownLat = lat;
    }

    function onPointerMove(event) {
        event.preventDefault();

        if (scope.isUserInteracting === true) {
            var clientX = event.clientX || event.touches[0].clientX;
            var clientY = event.clientY || event.touches[0].clientY;

            lon = (onMouseDownMouseX - clientX) * rotateSpeed + onMouseDownLon;
            lat = (clientY - onMouseDownMouseY) * rotateSpeed + onMouseDownLat;
        }
    }

    function onPointerUp() {
        scope.isUserInteracting = false;
    }

    function onDocumentMouseWheel(event) {
        var fov = scope.camera.fov + event.deltaY * 0.05 * zoomSpeed;

        scope.camera.fov = THREE.Math.clamp(fov, minZoom, maxZoom);

        scope.camera.updateProjectionMatrix();
    }

    function onTouchStart(event) {
        event.preventDefault();

        switch (event.touches.length) {
            case 1:
                scope.isUserInteracting = true;

                var clientX = event.clientX || event.touches[0].clientX;
                var clientY = event.clientY || event.touches[0].clientY;

                onMouseDownMouseX = clientX;
                onMouseDownMouseY = clientY;

                onMouseDownLon = lon;
                onMouseDownLat = lat;
                break;
            case 2:
                scope.isUserInteracting = true;
                var dx = event.touches[0].pageX - event.touches[1].pageX;
                var dy = event.touches[0].pageY - event.touches[1].pageY;

                var distance = Math.sqrt(dx * dx + dy * dy);
                dollyStart.set(0, distance);
                break;
        }
    }

    function onTouchMove(event) {
        event.preventDefault();
        event.stopPropagation();

        switch (event.touches.length) {
            case 1:
                if (scope.isUserInteracting === true) {
                    var clientX = event.clientX || event.touches[0].clientX;
                    var clientY = event.clientY || event.touches[0].clientY;

                    lon = (onMouseDownMouseX - clientX) * rotateSpeed + onMouseDownLon;
                    lat = (clientY - onMouseDownMouseY) * rotateSpeed + onMouseDownLat;
                }
                break;
            case 2:
                if (scope.isUserInteracting === true) {
                    var dx = event.touches[0].pageX - event.touches[1].pageX;
                    var dy = event.touches[0].pageY - event.touches[1].pageY;

                    var distance = Math.sqrt(dx * dx + dy * dy);

                    dollyEnd.set(0, distance);

                    dollyDelta.subVectors(dollyEnd, dollyStart);

                    var fov = scope.camera.fov - dollyDelta.y * 0.05 * zoomSpeed;

                    scope.camera.fov = THREE.Math.clamp(fov, minZoom, maxZoom);

                    scope.camera.updateProjectionMatrix();

                    dollyStart.copy(dollyEnd);
                }
                break;
        }
    }

    function onTouchEnd(event) {
        scope.isUserInteracting = false;
    }

    //
    scope.domElement.addEventListener(
        'dragover',
        function(event) {
            if (!scope.isDragPanoImage) return;
            event.preventDefault();
            event.dataTransfer.dropEffect = 'copy';
        },
        false
    );

    scope.domElement.addEventListener(
        'dragenter',
        function() {
            if (!scope.isDragPanoImage) return;

            document.body.style.opacity = 0.5;
        },
        false
    );

    scope.domElement.addEventListener(
        'dragleave',
        function() {
            if (!scope.isDragPanoImage) return;

            document.body.style.opacity = 1;
        },
        false
    );

    scope.domElement.addEventListener(
        'drop',
        function(event) {
            if (!scope.isDragPanoImage) return;

            event.preventDefault();

            var reader = new FileReader();
            reader.addEventListener(
                'load',
                function(event) {
                    material.map.image.src = event.target.result;
                    material.map.needsUpdate = true;
                },
                false
            );
            reader.readAsDataURL(event.dataTransfer.files[0]);

            document.body.style.opacity = 1;
        },
        false
    );

    scope.domElement.addEventListener('mousedown', onPointerStart, false);
    scope.domElement.addEventListener('mousemove', onPointerMove, false);
    scope.domElement.addEventListener('mouseup', onPointerUp, false);

    scope.domElement.addEventListener('wheel', onDocumentMouseWheel, false);

    scope.domElement.addEventListener('touchstart', onTouchStart, false);
    scope.domElement.addEventListener('touchmove', onTouchMove, false);
    scope.domElement.addEventListener('touchend', onTouchEnd, false);

    this.dispose = function() {
        scope.domElement.removeEventListener('mousedown', onPointerStart, false);
        scope.domElement.removeEventListener('mousemove', onPointerMove, false);
        scope.domElement.removeEventListener('mouseup', onPointerUp, false);

        scope.domElement.removeEventListener('wheel', onDocumentMouseWheel, false);

        scope.domElement.removeEventListener('touchstart', onTouchStart, false);
        scope.domElement.removeEventListener('touchmove', onTouchMove, false);
        scope.domElement.removeEventListener('touchend', onTouchEnd, false);
    };

    this.update = function() {
        if (scope.isUserInteracting === false && scope.autoRotate) {
            lon += 0.1;
            lon = lon % 360;
        }

        lat = Math.max(-85, Math.min(85, lat));
        phi = THREE.Math.degToRad(90 - lat);
        theta = THREE.Math.degToRad(lon);

        if (scope.enableDamping === true) {
            sphericalDelta.theta *= 1 - scope.dampingFactor;
            sphericalDelta.phi *= 1 - scope.dampingFactor;
        } else {
            sphericalDelta.set(0, 0, 0);
        }

        phi += sphericalDelta.phi;
        theta += sphericalDelta.theta;

        scope.camera.target.x = 500 * Math.sin(phi) * Math.cos(theta);
        scope.camera.target.y = 500 * Math.cos(phi);
        scope.camera.target.z = 500 * Math.sin(phi) * Math.sin(theta);

        // scope.camera.target.x = 500 * Math.sin(phi) * Math.cos(theta);
        // scope.camera.target.y = 500 * Math.sin(phi) * Math.sin(theta);
        // scope.camera.target.z = 500 * Math.cos(phi);

        scope.camera.lookAt(camera.target);
    };
};
